""" TODO:
class BlockStreamWrapper(Stream):
    pass
class BlockStreamWriter(StreamWriter):
    pass
class BlockStreamReader(StreamReader):
    pass
"""

from __future__ import annotations
from utility import BitUtility

from abc import ABC
class AbstractBlock(ABC):
    def __init__(self, data: bytearray):
        self.__size = len(data)
        self.__data = data

    def size(self) -> int:
        return self.__size

    def resize(self, size: int):
        raise NotImplementedError("resize the data & byte")

    def data(self) -> bytearray:
        return self.__data

    def __repr__(self):
        return f"AbstractBlock=>size:[{self.__size}], data:[{self.__data}]:"
    

class Block(AbstractBlock):
    @staticmethod
    def from_size(size: int = 4096) -> Block:
        size = Block.__checked_align(size)
        return Block(bytearray(size))

    @staticmethod
    def from_byte(c: int, capacity: int = 4096, repeat: int = 1) -> Block:
        if isinstance(c, int) and c >= 0 and c <= 255:
            capacity = Block.__checked_align(capacity)
            if repeat <= capacity:
                ba = bytearray(capacity)
                ba[0:repeat] = (c for i in range(repeat))
                return Block(ba)
            else:
                raise ValueError(f"repeat:{repeat} > capacity:{capacity}")
        else:
            raise ValueError(f"c:{c} is not ascii")

    @staticmethod
    def from_str(s: str, codec: str ="utf-8") -> Block:
        bs = bytes(s, codec)
        cap = Block.__checked_align(len(bs))

        ba = bytearray(cap)
        ba[0:len(bs)] = (bs[i] for i in range(len(bs)))
        return Block(ba)

    def __init__(self, data: bytearray):
        super().__init__(data)

    @staticmethod
    def __checked_align(size: int) -> int:
        return BitUtility.align_greater_equal(size, 4096)

    def __repr__(self):
        return f"Block=>size:[{self.size()}], data:[{self.data()}]:"


import unittest
class TestBlock(unittest.TestCase):
    def test_from_size(self):
        b = Block.from_size(512)
        self.assertEqual(4096, b.size())
        b.data()[0] = 0x01
        self.assertEqual(0x01, b.data()[0])

    def test_from_byte(self):
        v = ord('a')
        b = Block.from_byte(v)
        self.assertEqual(4096, b.size())
        self.assertEqual(v, b.data()[0])
        self.assertEqual(0x00, b.data()[1])

        b = Block.from_byte(v, 1024, 10)
        self.assertEqual(4096, b.size())
        for i in range(10):
            self.assertEqual(v, b.data()[i])
        self.assertEqual(0x00, b.data()[10])

    def test_from_str(self):
        s = 'hello world!'

        b = Block.from_str(s)
        self.assertEqual(4096, b.size())
        for i in range(len(s)):
            self.assertEqual(ord(s[i]), b.data()[i])
        self.assertEqual(0x00, b.data()[len(s)+1])

        s_ch = '你好'
        s_ch_bytes = s_ch.encode('gbk')

        b_ch = Block.from_str(s_ch, 'gbk')
        self.assertEqual(4096, b_ch.size())
        for i in range(len(s_ch_bytes)):
            self.assertEqual(s_ch_bytes[i], b_ch.data()[i])
        self.assertEqual(0x00, b_ch.data()[len(s_ch_bytes)+1])



class Blocks(object):
    @staticmethod
    def from_list(list_blocks: list[Block]):
        if len(list_blocks) > 0:
            bs = Blocks(list_blocks[0].size())
            for b in list_blocks:
                bs.append(b)
            return bs
        else:
            return Blocks() 

    def __init__(self, block_size: int = 4096):
        self.__block_size = block_size
        self.__data = []

    def block_size(self):
        return self.__block_size

    def __len__(self):
        return len(self.__data)

    def number_of_bytes(self):  
        return len(self) * self.__block_size



    def append(self, block: Block) -> bool:
        if self.__block_size ==  block.size():
            self.__data.append(block)
            return True
        return False


    def remove(self, index: int) -> Block:
        if index >= 0 and index < len(self):
            return self.__data.pop(index)
        else:
            raise ValueError("index:[{}] out of range:[0,{})".format(index, len(self)))

    def get(self, index: int) -> Block:
        if index >= 0 and index < len(self):
            return self.__data[index]
        else:
            raise ValueError("index:[{}] out of range:[0,{})".format(index, len(self)))

    def clear(self):
        self.__data = {}

    class Iterator(object):
        def __init__(self, host):
            self.__host = host
            self.__index = 0

        def __next__(self):
            if self.__index < len(self.__host):
                v = self.__host.get(self.__index)
                self.__index = self.__index + 1
                return v
            else:
                raise StopIteration

    def __iter__(self) -> Blocks.Iterator:
        return Blocks.Iterator(self)



    """
    def push_back(self, block: Block) -> bool:
        pass
    def pop_front(self) -> Block | None:
        pass
    def push_front(self, block: Block) -> bool:
        pass
    def pop_back(self) -> Block | None:
        pass
    """


class TestBlocks(unittest.TestCase):
    def test_basement(self):
        bs = Blocks(4096)

        b = Block.from_size(4096)
        r = bs.append(b)
        self.assertTrue(r)

        b2 = Block.from_size(4096)
        r = bs.append(b2)
        self.assertTrue(r)

        b3 = Block.from_size(4096)
        r = bs.append(b3)
        self.assertTrue(r)

        sz = len(bs)
        self.assertEqual(3, sz)

        nbytes = bs.number_of_bytes()
        self.assertEqual(3* 4096, nbytes)

        bs.remove(0)
        b = bs.get(0)
        self.assertEqual(b2, b)
    def test_iter(self):
        bs = Blocks()
        bs.append(Block.from_size())
        bs.append(Block.from_size())
        bs.append(Block.from_size())

        for b in bs:
            print(id(b))

    def test_from_list(self):
        bs = Blocks.from_list([Block.from_size(8192),])
        self.assertEqual(1, len(bs))
        self.assertEqual(8192, bs.block_size())

        bs = Blocks.from_list([])
        self.assertEqual(0, len(bs))
        self.assertEqual(4096, bs.block_size())
